/*******************************************************************************
 * SVN­Versioning-Information:
 * ­­­­­­­­­­­­­­­­­­­­­­­­
 * $LastChangedRevision: 6 $
 * $LastChangedBy: c.a.duevel $
 * $LastChangedDate: 2009-08-26 15:12:37 +0000 (Wed, 26 Aug 2009) $
 * $HeadURL: http://robofight.googlecode.com/svn/trunk/Server/src/de/hft_stuttgart/botwar/server/remote/Login.java $
 ******************************************************************************/
package de.hft_stuttgart.botwar.server.remote;

import de.hft_stuttgart.botwar.common.commands.ChatCmd;
import de.hft_stuttgart.botwar.common.commands.KickCmd;
import de.hft_stuttgart.botwar.common.interfaces.IRemoteLogin;
import de.hft_stuttgart.botwar.common.interfaces.IRemotePlayer;
import de.hft_stuttgart.botwar.common.interfaces.IRemotePlayer.CHATTYPE;
import de.hft_stuttgart.botwar.common.models.Password;
import de.hft_stuttgart.botwar.exceptions.RegistrationUnsuccessfulException;
import de.hft_stuttgart.botwar.server.DataBase;
import de.hft_stuttgart.botwar.server.Main;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.security.auth.login.LoginException;
import org.apache.log4j.Logger;

/**
 *
 * @author c.a.duevel@gmail.com, pmv-mail@gmx.de, sdegiorgi@gmail.com
 */
public class Login implements IRemoteLogin {

    private static Logger logger = Logger.getLogger(Login.class);
    static Map<String, RemotePlayer> onlinePlayers =
                                        new HashMap<String, RemotePlayer>();
    DataBase db;

    public Login() {
        db = getDataBase();
    }

    /**
     *
     * @param id id of the calling user
     * @param pwd pwd of the calling user
     * @return IRemotePlayer object needed for starting a game
     * @throws java.rmi.RemoteException
     */
    public IRemotePlayer login(final String id, final String pwd)
                    throws RemoteException, LoginException {
        if (onlinePlayers.get(id) != null) {
            throw new LoginException(String.format(
                                            "User %s is already logged in",id));
        }
        RemotePlayer rPlayer = db.login(id, pwd);
        Object returnV = onlinePlayers.put(id, rPlayer);
        assert returnV == null : "Can't be logged in already";
        IRemotePlayer player = (IRemotePlayer) UnicastRemoteObject.exportObject(
                rPlayer, Main.getRegistryPort());
        logger.info("Player " + id + " has logged in");

        rPlayer.addCommand(new ChatCmd(CHATTYPE.LOBBY,
                      Main.getSystemName(),
                      "Welcome " + rPlayer.getID() + "! " +
                      "To write a personal message enter \"/Username:Message\"."
                      ));

        return player;
    }

    public void register(final String userid,
                         final String pwd,
                         final String email)
                    throws RemoteException, RegistrationUnsuccessfulException {
        if (userid == null || pwd == null || email == null) {
            throw new IllegalArgumentException("arguments can't be null");
        }

        if (pwd.length() < 5) {
            throw new IllegalArgumentException(
                                        "Password to short! (under 5 chars)");
        }

        if (userid.length() < 3) {
            throw new IllegalArgumentException(
                                        "Username to short! (under 3 chars)");
        }

        if (userid.length() > 32) {
            throw new IllegalArgumentException(
                                        "username to long! (over 32 chars)");
        }

        if (!email.matches("[^\\s@]+@[^\\s@.]+\\.[A-Za-z]+")) {
            throw new IllegalArgumentException(
                                        "\"" + email + "\" isn't an e-mail");
        }

        if (!isNameAllowed(userid)){
            throw new RegistrationUnsuccessfulException(
                    String.format("username \"%s\" not allowed", userid));
        }
        db.register(userid, pwd, email);
    }

    DataBase getDataBase() {
        return DataBase.get();
    }

    public static void kickLaggingClients(long intervall) {
        List<RemotePlayer> kickPlayer = new ArrayList<RemotePlayer>(2);
        for (RemotePlayer player : onlinePlayers.values()) {
            if (player.getLastPoll().getTimeInMillis() + intervall <=
                    Calendar.getInstance().getTimeInMillis()) {
                kickPlayer.add(player);
            }
        }

        for (RemotePlayer player : kickPlayer) {
            //TODO: What must also be done, when a client is lost?
            logout(player);
            player.addCommand(new KickCmd("Server (Timeout)", player.getID()));
        }
    }
    
    public static boolean isLoggedIn(String id){
        return onlinePlayers.containsKey(id);
    }

    static void logout(RemotePlayer player){
        if (player.getGame() != null){
            player.getGame().removePlayer(player);
        }
        onlinePlayers.remove(player.getID());
        getDB().logout(player.getPlayer());
        logger.debug(String.format("Player %s is logged off", player.getID()));

    }

    public static List<RemotePlayer> loggedInPlayers(){
        return new ArrayList(onlinePlayers.values());
    }

    static RemotePlayer getPlayer(final String id){
        return onlinePlayers.get(id);
    }

    static List<String> getLoggedInPlayerIDs(){
        return new ArrayList<String>(onlinePlayers.keySet());
    }

    static DataBase getDB(){
        return DataBase.get();
    }

    private boolean isNameAllowed(String name) {
        String[] notAllowedNames = { 
                       Main.getSystemName(), //name used for systemmessages
                       "lobby"      //used for chatting from gamelobby to lobby
                       };

        //no whitspaces, except space
        if (name.matches("(.*[\\s]+.*)+")){
            String str = "";
            for(int i = 0; i < name.length(); i++){
                String letter = name.substring(i, i + 1);
                if (!letter.matches("\\s")){
                    str += letter;
                }
            }
            str = str.trim();
            if (str.length() < 2){
                return false;
            }
         }

        //check not allowed names
        for (int i = 0; i < notAllowedNames.length; i++) {
            if (notAllowedNames[i].equalsIgnoreCase(name)){
                return false;
            }
        }

        //no trailing slash allowed
        if (name.startsWith("/")){
            return false;
        }

        //no doublepoint allowed
        if (name.contains(":")){
            return false;
        }

        return true;
    }

    public IRemotePlayer login(String userid, Password pwd)
                    throws RemoteException, LoginException {
        return login(userid, pwd.toString());
    }

    public void register(String userid, Password pwd, String email)
                    throws RemoteException, RegistrationUnsuccessfulException {
        register(userid, pwd.toString(), email);
    }


}
